<?php

namespace App\Home\Console\Commands;

use App\Admin\Models\SystemModel;
use Illuminate\Console\GeneratorCommand;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Artisan;

class ResourceMakeCommand extends GeneratorCommand
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = '
        home:make-resource
        {slug : 数据表名称}
        {--force : 强制覆盖}
        {--model= : 指定模型}
        {--test : 生成控制器测试类}
    ';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = '添加一个前台资源，包含各种相关文件';

    protected $types = [
        'model', 'filter', 'request', 'resource', 'controller', 'showdoc', 'test',
    ];

    /**
     * 当前正在生成的类型
     *
     * @var string
     */
    protected $nowType;

    /**
     * 当前正在生成的数据库表名
     *
     * @var string
     */
    protected $title;

    /**
     * 各类型对应的完整的类名
     *
     * @var array
     */
    protected $classes = [];

    /**
     * Execute the console command.
     */
    public function handle()
    {   
        if (config()->get('app.env')=='production') {
            $this->warn('生产环境下不适用');
            return false;
        }
        $slug = $this->argument('slug');
        if (!Schema::hasTable($slug)) {
            $this->error("不存在到数据库表 [ {$slug} ]");
            return false;
        }
        // 判断数据表名称是否为复数
        $pluralKebabDummyResource = Str::plural(Str::kebab($slug));
        if ($pluralKebabDummyResource!=$slug) {
            $this->error("请把数据表名称 [ {$slug} ] 更改为 [ {$pluralKebabDummyResource} ]");
            return false;
        }
        $comment = DB::getDoctrineSchemaManager()->listTableDetails($slug)->getComment();
        if (!$comment) {
            $this->error("数据库表 [ {$slug} ] 没有备注");
            return false;
        }
        $requiredFields = array_unique(array_merge(
            SystemModel::$searchHideField,
            SystemModel::$listReturnField,
            SystemModel::$formHideField,
            SystemModel::$detailHideField
        ));
        // 判断数据表是否包含必要字段
        foreach ($requiredFields as $requiredField) {
            if (!Schema::hasColumn($slug, $requiredField)) {
                $this->error("数据库表 [ {$slug} ] 没有 [ {$requiredField} ] 字段，请创建");
                return false;
            }
        }
        $this->title = str_replace('表', '', $comment);
        $this->name = underlineToBar(Str::singular(Str::kebab($this->argument('slug'))));
        Artisan::call("admin:make-model ".$slug.' --force');
        DB::beginTransaction();
        try {
            $this->makeBackend();// 创建后端文件
            $this->makeBackRoute();// 创建后端路由
            DB::commit();
            return true;
        } catch (\Exception $e) {
            Log::error($e->getMessage());
            DB::rollBack();  
            return false;
        }
    }

    protected function makeBackend()
    {
        foreach ($this->types as $type) {
            $this->nowType = $type;
            $this->type = Str::ucfirst($type);

            if (($type == 'model') && ($model = $this->option('model'))) {
                if (!class_exists($model)) {
                    $this->error("模型 [ {$model} ] 不存在");
                    return false;
                } elseif (class_exists($model)) {
                    $this->classes[$type] = trim($model, '\\');
                    continue;
                }
            }

            if (($type == 'test') && (!$this->option('test'))) {
                continue;
            }

            if (parent::handle() === false) {
                continue;
            }
            $this->classes[$type] = $this->qualifyClass($this->getNameInput());
        }

        return true;
    }

    protected function getStub()
    {
        return __DIR__."/stubs/{$this->nowType}.stub";
    }

    protected function getNameInput()
    {
        $name = Str::studly(trim($this->name));

        if ($this->nowType == 'test') {
            $name = 'Feature\\'.$name.'ControllerTest';
        } elseif ($this->nowType != 'model') {
            $name .= $this->type;
        }

        return $name;
    }

    protected function rootNamespace()
    {
        return 'App\\Home\\'.Str::ucfirst(Str::plural($this->nowType));
    }

    protected function getPath($name)
    {
        $name = Str::replaceFirst($this->rootNamespace(), '', $name);
        return $this->laravel['path'].
            '/Home/'.
            Str::ucfirst(Str::plural($this->type)).
            str_replace('\\', '/', $name).'.php';
    }

    protected function replaceClass($stub, $name)
    {
        $stub = parent::replaceClass($stub, $name);
        if ($this->nowType == 'test') {
            $stub = str_replace('NamespacedDummyModel', $this->classes['model'], $stub);
            $stub = str_replace('dummy-resource-name', Str::plural($this->name), $stub);
        } elseif ($this->nowType == 'controller' || $this->nowType == 'showdoc') {
            foreach (['filter', 'request', 'resource', 'model'] as $type) {
                $stub = $this->replaceDummyResource($type, $stub);
            }
        } elseif (in_array($this->nowType, ['filter', 'request'])) {
            $stub = $this->replaceDummyResource('model', $stub);
        } 
        $stub = $this->replaceMethod($stub);
        return $stub;
    }

    protected function replaceDummyResource(string $type, string $stub)
    {
        $name = trim($this->name);
        $title = trim($this->title);
        $singularDummyResource = Str::singular(barToUnderline($name));
        $pluralKebabDummyResource = Str::plural(Str::kebab($name));
        $param = SystemModel::formatParam($pluralKebabDummyResource);
        $returnParam = SystemModel::formatReturnParam($pluralKebabDummyResource);
        $jsonParam = SystemModel::formatJsonParam($pluralKebabDummyResource);
        $return = str_replace('@json_param', '@return', $jsonParam);
        $namespaced = $this->classes[$type];
        $class = class_basename($namespaced);
        $type = Str::ucfirst($type);
        $stub = str_replace("NamespacedDummy{$type}", $namespaced, $stub);
        $stub = str_replace("Dummy{$type}", $class, $stub);
        $stub = str_replace("DummyTitle", $title, $stub);
        $stub = str_replace("dummy-resources", $pluralKebabDummyResource, $stub);
        $stub = str_replace("dummy_resource", $singularDummyResource, $stub);
        $stub = str_replace("showDummyDoc", "showdoc", $stub);

        $stub = str_replace("* store@json_param {}", $jsonParam, $stub);
        $stub = str_replace("* store@param 参数名 是否必选 类型 说明", $param, $stub);
        $stub = str_replace("* store@return {}", $return, $stub);
        $stub = str_replace("* store@return_param 参数名 类型 说明", $returnParam, $stub);
        

        $stub = str_replace("* update@json_param {}", $jsonParam, $stub);
        $stub = str_replace("* update@param 参数名 是否必选 类型 说明", $param, $stub);
        $stub = str_replace("* update@return {}", $return, $stub);
        $stub = str_replace("* update@return_param 参数名 类型 说明", $returnParam, $stub);

        $stub = str_replace("* destroy", "* ", $stub);

        $stub = str_replace("* index@param 参数名 是否必选 类型 说明", $param, $stub);
        $stub = str_replace("* index@return {}", $return, $stub);
        $stub = str_replace("* index@return_param 参数名 类型 说明", $returnParam, $stub);

        $stub = str_replace("* show@param 参数名 是否必选 类型 说明", '* @param 参数名 是否必选 类型 说明', $stub);
        $stub = str_replace("* show@return {}", $return, $stub);
        $stub = str_replace("* show@return_param 参数名 类型 说明", $returnParam, $stub);

        $stub = str_replace("* @catalog 接口简介/", "* @catalog ".config()->get('app.name')."接口简介/", $stub);

        if ($type == 'Model') {
            $model = '$'.Str::camel($class);
            $models = Str::plural($model);

            $stub = str_replace('$dummyModel', $model, $stub);
            $stub = str_replace('$dummyModels', $models, $stub);
        }

        return $stub;
    }

    protected function replaceMethod(string $stub)
    {
        $slug = Str::plural($this->name);
        $parentId = SystemModel::getIdBySlug($slug);
        $childrenField = SystemModel::childrenField($parentId);
        $filterRules = json_encode(
            SystemModel::getFilterRules($childrenField), 
            JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT
        );
        $fieldTypes = json_encode(
            SystemModel::getFieldTypes($childrenField), 
            JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT
        );
        $fieldTypes = str_replace("\\", "\\\\", $fieldTypes);
        $validationRules = json_encode(
            SystemModel::getValidationRules($childrenField), 
            JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT
        );
        $filterRulesMethod = <<< METHOD
        public \$filterRules = <<< EOF
        $filterRules
        EOF;   
        METHOD;
        $fieldTypesMethod = <<< METHOD
        public \$fieldTypes = <<< EOF
        $fieldTypes
        EOF;   
        METHOD;
        $validationRulesMethod = <<< METHOD
        public \$validationRules = <<< EOF
        $validationRules
        EOF;   
        METHOD;
        $stub = str_replace('public $filterRules;', $filterRulesMethod, $stub);
        $stub = str_replace('public $fieldTypes;', $fieldTypesMethod, $stub);
        $stub = str_replace('public $validationRules;', $validationRulesMethod, $stub);
        return $stub;
    }

    /**
     * 创建后端路由
     */
    protected function makeBackRoute()
    {
        $name = trim($this->name);
        $dummyResource = Str::camel($name);
        $ucDummyResource = Str::ucfirst($dummyResource);
        $pluralKebabDummyResource = Str::plural(Str::kebab($name));
        $controller = "C\\".$ucDummyResource."Controller::class";
        $replace = PHP_EOL."Route::resource('{$pluralKebabDummyResource}', $controller);";
        $path = "routes/home.php";
        $this->info("请在 {$path} 手动新增后端路由：".$replace);
    }
}
